/**
 * Automatic Subtitle Downloader
 * http://code.google.com/p/autosubdown/
 * 
 * Copyright 2010-2011 Raphael Medeiros.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 */
package com.byraphaelmedeiros.autosubdown;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.social.twitter.api.Tweet;
import org.springframework.social.twitter.api.TwitterApi;
import org.springframework.social.twitter.api.impl.TwitterTemplate;

import com.byraphaelmedeiros.autosubdown.domain.Config;
import com.byraphaelmedeiros.autosubdown.domain.Provider;
import com.byraphaelmedeiros.autosubdown.domain.ProviderType;
import com.byraphaelmedeiros.autosubdown.domain.Rule;
import com.byraphaelmedeiros.autosubdown.util.RarUtils;
import com.byraphaelmedeiros.autosubdown.util.ZipUtils;

import de.nava.informa.core.ChannelIF;
import de.nava.informa.core.ItemIF;
import de.nava.informa.core.ParseException;
import de.nava.informa.impl.basic.ChannelBuilder;
import de.nava.informa.parsers.FeedParser;

/**
 * @author Raphael Medeiros
 *
 */
public class AutoSubDown {
	
	private static final Object ZIP = "zip";
	
	private static final Object RAR = "rar";

	private static TwitterApi TAPI = new TwitterTemplate();
	
	private static Logger LOGGER = Logger.getLogger(AutoSubDown.class.getName());
	
	private static Thread autoSubDownThread = new Thread(new AutoSubDownThread());
	
	/**
	 * Efetua a leitura dos provedores e download dos arquivos
	 */
	public static void download() {
		LOGGER.log(Level.INFO, "Starting downloads...");
		
		Config config = Config.getInstance();
		
		config.load();
		
		List<Provider> providers = config.getProviders();
		
		if (!providers.isEmpty()) {
			LOGGER.log(Level.INFO, "Retrieving providers (twitter/rss)...");
			
			Rule rule = null;
			
			for (Provider p : providers) {
				if (ProviderType.TWITTER.equals(p.getType())) {
					List<Tweet> userTimeline = TAPI.timelineOperations().getUserTimeline(p.getName());
					
					if (userTimeline != null && !userTimeline.isEmpty()) {
						LOGGER.log(Level.INFO, "Recovering messages for " + StringUtils.capitalize(p.getName()) + "...");
						
						String content = null;
						
						for (Tweet t : userTimeline) {
							content = t.getText();
							
							//content = "Nova legenda disponivel: http://www.insubs.com/legendas/the.big.bang.theory.S02E09.DVDRip.xVid.InSubs.zip #AutoSubDown";
							
							if (config.inRule(content)) {
								LOGGER.log(Level.INFO, "Valid message!");
								
								rule = config.getRule(content);
								
								download(rule, content);
								
								if (rule.isTVShow()) {
									rule.addEpisodeDownloaded(rule.getEpisode(content));
									
									config.save();
								}
							}
						}
					}
					
				} else if (ProviderType.RSS.equals(p.getType())) {
					String title = null, link = null;
					
					try {
						ChannelIF rssChannel = FeedParser.parse(new ChannelBuilder(), new URL(p.getUrl()));
						
						Collection<ItemIF> items = rssChannel.getItems();
						
						if (items != null && !items.isEmpty()) {
							LOGGER.log(Level.INFO, "Recovering messages for " + StringUtils.capitalize(p.getName()) + "...");
							
							for (ItemIF item : items) {
								title = item.getTitle();
								
								if (config.inRule(title)) {
									LOGGER.log(Level.INFO, "Valid message!");
									
									rule = config.getRule(title);
									
									if (item.getLink() != null) {
										link = item.getLink().toString();
										
										downloadTo(rule, link);
										extract(config, rule, link);
										
										if (rule.isTVShow()) {
											rule.addEpisodeDownloaded(rule.getEpisode(title));
											
											config.save();
										}
									} else {
										LOGGER.log(Level.INFO, "Invalid link!");
									}
								}
							}
						}
					} catch (MalformedURLException e) {
						//e.printStackTrace();
						LOGGER.log(Level.WARNING, "Invalid URL! (" + link + ")");
					} catch (IOException e) {
						//e.printStackTrace();
						LOGGER.log(Level.WARNING, "Invalid URL! (" + link + ")");
					} catch (ParseException e) {
						//e.printStackTrace();
						LOGGER.log(Level.WARNING, "Invalid URL! (" + link + ")");
					}
				}
			}
		}
		
		/*
		List<SiteProvider> siteProviders = config.getSiteProviders();
		
		if (!siteProviders.isEmpty()) {
			LOGGER.log(Level.INFO, "Retrieving site providers (websites)...");
			
			try {
				Rule rule = null;
				
				HttpClient httpClient = new DefaultHttpClient();
				HttpContext httpContext = new BasicHttpContext();
				
				HttpPost httpPost = null;
				UrlEncodedFormEntity entityPost = null;
				HttpResponse httpResponse = null;
				HttpEntity entityResponse = null;
				String htmlContent = null;
				
				// action = login_verificar.php, txtLogin, txtSenha
				
				List<NameValuePair> params = new ArrayList<NameValuePair>();
				params.add(new BasicNameValuePair("param1", "value1"));
				params.add(new BasicNameValuePair("param2", "value2"));
				
				for (SiteProvider sp : siteProviders) {
					entityPost = new UrlEncodedFormEntity(params, "UTF-8");
					
					httpPost = new HttpPost(sp.getUrlLoginAction());
					httpPost.setEntity(entityPost);
					
					httpResponse = httpClient.execute(httpPost, httpContext);
					
					entityResponse = httpResponse.getEntity();
					
					htmlContent = EntityUtils.toString(entityResponse);
					
					// pegar link do botao DOWNLOAD
					
					// executar link do botao DOWNLOAD e salvar o arquivo
				}
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
				//LOGGER.log(Level.WARNING, "Invalid URL! (" + link + ")");
			} catch (ClientProtocolException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		*/
		
		LOGGER.log(Level.INFO, "Done!");
	}

	/**
	 * @param config
	 * @param rule
	 * @param link
	 */
	private static void extract(Config config, Rule rule, String link) {
		if (config.isExtract()) {
			String extension = FilenameUtils.getExtension(link).toLowerCase();
			String filePath = rule.getDownloadTo() + File.separator + rule.getFileName() + "." + extension;
			
			if (ZIP.equals(extension)) {
				LOGGER.log(Level.INFO, "Extracting ZIP data for " + filePath + "...");
				
				ZipUtils.unzipFile(filePath, null);
				
			} else if (RAR.equals(extension)) {
				LOGGER.log(Level.INFO, "Extracting RAR data for " + filePath + "...");
				
				RarUtils.unrarFile(filePath, null);
				
			} else {
				LOGGER.log(Level.INFO, "Unsupported file type! (" + extension.toUpperCase() + ")");
			}
		}
	}

	private static void download(Rule rule, String content) {
		String link = extractLink(content);
		
		if (link != null)
			downloadTo(rule, link);
	}

	private static void downloadTo(Rule rule, String link) {
		LOGGER.log(Level.INFO, "Downloading " + link +"...");
		
		try {
			String extension = FilenameUtils.getExtension(link);
			BufferedInputStream bis = new BufferedInputStream(new URL(link).openStream());
			FileOutputStream fos = new FileOutputStream(rule.getDownloadTo() + File.separator + rule.getFileName() + "." + extension);
			BufferedOutputStream bos = new BufferedOutputStream(fos, 1024);
			
			byte data[] = new byte[1024];
			int x = 0;
			
			while ((x = bis.read(data, 0, 1024)) >= 0) {
				bos.write(data, 0, x);
			}
			
			bos.close();
			bis.close();
		} catch (MalformedURLException e) {
			//e.printStackTrace();
			LOGGER.log(Level.WARNING, "Invalid URL! (" + link + ")");
		} catch (IOException e) {
			//e.printStackTrace();
			LOGGER.log(Level.WARNING, "Unable to access the URL (" + link + ").");
		}
	}

	private static String extractLink(String content) {
		LOGGER.log(Level.INFO, "Extracting URLs...");
		
		List<String> urls = extractUrls(content);
		
		if (!urls.isEmpty())
			return urls.iterator().next();
			
		return null;
	}
	
	private static List<String> extractUrls(String content) {
        List<String> urls = new ArrayList<String>();

        Pattern pattern = Pattern.compile(
            "\\b(((ht|f)tp(s?)\\:\\/\\/|~\\/|\\/)|www.)" + 
            "(\\w+:\\w+@)?(([-\\w]+\\.)+(com|org|net|gov" + 
            "|mil|biz|info|mobi|name|aero|jobs|museum" + 
            "|travel|[a-z]{2}))(:[\\d]{1,5})?" + 
            "(((\\/([-\\w~!$+|.,=]|%[a-f\\d]{2})+)+|\\/)+|\\?|#)?" + 
            "((\\?([-\\w~!$+|.,*:]|%[a-f\\d{2}])+=?" + 
            "([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)" + 
            "(&(?:[-\\w~!$+|.,*:]|%[a-f\\d{2}])+=?" + 
            "([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)*)*" + 
            "(#([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)?\\b");

        Matcher matcher = pattern.matcher(content);
        
        while (matcher.find()) {
            urls.add(matcher.group());
        }

        return urls;
    }

	/**
	 * Iniciar o MODO deamon
	 */
	public static void deamon(boolean start) {
		if (start) {
			autoSubDownThread.setDaemon(true);
			autoSubDownThread.start();
		} else {
			autoSubDownThread.interrupt();
		}
	}
}